package com.alfy.websocketdemo;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket 服务端
 */
@ServerEndpoint(value = "/websocket/{userName}", configurator = CustomConfigurator.class)
public class WebSocketServer {

    /**
     * 客户端计数器
     */
    private static final AtomicInteger counter = new AtomicInteger(0);

    /**
     * 客户端 WebSocket 连接集合
     */
    private static final Set<WebSocketServer> connections = new CopyOnWriteArraySet<>();

    /**
     * 当前 WebSocket 会话对象
     */
    private Session session = null;

    /**
     * 客户端编号
     */
    private Integer number = 0;

    /**
     * 构造器
     */
    public WebSocketServer() {
        number = counter.incrementAndGet() - 1;
    }

    /**
     * open 事件, 客户端建立 WebSocket 连接
     *
     * @param session  会话对象
     * @param userName 端点路径参数
     * @throws IOException ioexception
     */
    @OnOpen
    public void open(Session session, @PathParam("userName") String userName) throws IOException {
        System.out.println("open 事件 ==> 欢迎用户: " + userName + "进入房间, 地址参数：" + session.getQueryString());
        this.session = session;
        connections.add(this);
        session.getBasicRemote().sendText("系统提示: " + userName + "进入房间");
    }

    /**
     * message 事件, 接收客户端发送的消息
     *
     * @param session  会话对象
     * @param message  消息, 字符串
     * @param userName 端点路径参数
     * @throws IOException ioexception
     */
    @OnMessage
    public void message(Session session, String message, @PathParam("userName") String userName) {
        if (session.isOpen()) {
            try {
                System.out.println("message 事件 ==> 服务器接收到 " + userName + " 用户的消息: " + message);
                // 往其他客户端发送最新消息
                for (WebSocketServer client : connections) {
                    synchronized (client) {
                        if (client.session.getId().equals(session.getId())) {
                            continue;
                        }
                        client.session.getBasicRemote().sendText("接收到消息: " + message);
                    }
                }
                // 返回发送成功消息确定
                session.getBasicRemote().sendText("系统提示: 发送成功");
            } catch (IllegalStateException e) {
                System.out.println("message 事件 ==> Failed to send message, session is closed, ex: " + e.getMessage());
            } catch (IOException e) {
                System.out.println("（message）ex：" + e.getMessage());
            }
        } else {
            System.out.println("message 事件 ==> Cannot send message, session is closed");
        }
    }

    /**
     * message 事件, 接收客户端发送的消息
     *
     * @param session  会话对象
     * @param message  消息, 二进制
     * @param userName 端点路径参数
     */
    @OnMessage
    public void message(Session session, ByteBuffer message, @PathParam("userName") String userName) {
        // 处理二进制消息
         System.out.println("message 事件 ==> 接收到二进制消息, 长度: " + message.remaining() + ", message: " + new String(message.array()));
    }

    /**
     * close 事件, 客户端断开 WebSocket 连接
     *
     * @param session     会话对象
     * @param closeReason 关闭原因
     * @param userName    端点路径参数
     */
    @OnClose
    public void close(Session session, CloseReason closeReason, @PathParam("userName") String userName) {
        if (session.isOpen()) {
            try {
                System.out.println("close 事件 ==> " + userName + " 用户退出房间, closeReason: " + closeReason);
                session.close();
            } catch (IOException e) {
                System.out.println("close 事件 ==> ex: " + e.getMessage());
            } catch (IllegalStateException e) {
                System.out.println("close 事件 ==> Failed to close session, session is already closed, ex: " + e.getMessage());
            } finally {
                // 移除记载
                connections.remove(this);
            }
        } else {
            System.out.println("close 事件 ==> Session is already closed");
        }
    }

    /**
     * error 事件
     *
     * @param session   session
     * @param throwable throwable
     * @param userName  user name
     */
    @OnError
    public void error(Session session, Throwable throwable, @PathParam("userName") String userName) {
        System.out.println(userName + "连接异常（error 事件）, number: " + number + ", exMessage" + throwable.getMessage()
                + ", istrue: " + (this.session.getId().equals(session.getId())));
    }

    /**
     * 获取所有连接客户端集合
     *
     * @return {@link Set }<{@link WebSocketServer }>
     */
    public static Set<WebSocketServer> getConnections() {
        return connections;
    }

    /**
     * 获取当前会话对象
     *
     * @return {@link Session }
     */
    public Session getSession() {
        return session;
    }

}
